package org.flashbrighton.as3bots.controllers
{
	import Box2D.Collision.*;
	import Box2D.Collision.Shapes.*;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.*;
	
	import flash.display.*;
	import flash.events.Event;
	
	import org.flashbrighton.as3bots.defs.*;
	import org.flashbrighton.as3bots.events.ContactListener;
	import org.flashbrighton.as3bots.logic.*;
	import org.flashbrighton.as3bots.models.*;
	import org.flashbrighton.as3bots.views.*;
	
	import uk.co.steamboy.utils.DynamicTimer;


	public class GameController extends Sprite
	{		
		private const MAX_SPEED:int = 250;
		private const MAX_TORQUE:int = 30;
		
		private var _model:GameModel;
		private var _view:GameView;
		private var _contactListener:ContactListener;
		private var _dynamicTimer:DynamicTimer;
		
		public var debugBot:b2Body;


		public function GameController(model:GameModel, view:GameView):void
		{
			_model = model;
			_view = view;
			init();
		}

		
		private function init():void
		{							
			createArena();
			createBots();
			_view.addViews();
			_view.setUI();
			
			_contactListener = new ContactListener();
			_model.world.SetContactListener(_contactListener);
			_model.isPlaying = true;
			_model.updateWorld();
			
			//addEventListener(Event.ENTER_FRAME, update, false, 0, true);
			_dynamicTimer = new DynamicTimer(60);
			_dynamicTimer.registerUpdater(update);
			_dynamicTimer.registerRenderer(_view.update);
			_dynamicTimer.start();
		}
		
		
		private function update(event:Event=null):void
		{	
			if (!_model.isPlaying)
			{
				return;
			}
			
			moveBots();
			_model.updateWorld();
			checkScores();
		}
		
		
		private function createArena():void
		{
			var density:Number = 0;
			var friction:Number = 0.5;
			var restitution:Number = 0.5;
			
			var topWall:b2Body;
			var bottomWall:b2Body;
			var leftWall:b2Body;
			var rightWall:b2Body;
			
			var topModel:WallModel = new WallModel();
			var bottomModel:WallModel = new WallModel();
			var leftModel:WallModel = new WallModel();
			var rightModel:WallModel = new WallModel();
			
			topModel.width = 14.5;
			topModel.height = 0.3;
			bottomModel.width = 14.5;
			bottomModel.height = 0.3;
			leftModel.width = 0.3;
			leftModel.height = 9.0;
			rightModel.width = 0.3;
			rightModel.height = 9.0;
			
			topWall = createBox(topModel.width/2, topModel.height/2, topModel.width, topModel.height, density, friction, restitution, topModel);
			bottomWall = createBox(topModel.width/2, leftModel.height+bottomModel.height*1.5, bottomModel.width, bottomModel.height, density, friction, restitution, bottomModel);
			leftWall = createBox(leftModel.width/2, leftModel.height/2+topModel.height, leftModel.width, leftModel.height, density, friction, restitution, leftModel);
			rightWall = createBox(topModel.width-rightModel.width/2, leftModel.height/2+topModel.height, rightModel.width, rightModel.height, density, friction, restitution, rightModel);
			
			_model.addWall(topWall, new WallGraphic());
			_model.addWall(bottomWall, new WallGraphic());
			_model.addWall(leftWall, new WallGraphic());
			_model.addWall(rightWall, new WallGraphic());
		}
		
		
		private function createBots():void
		{
			//TODO: BotDef - logic, graphic, colour, name, radius - for now radius determines mass
			registerBot(new JoesBot());
			registerBot(new NeilsBot());
			registerBot(new SebsBot());
			registerBot(new OwensBot());
			/*registerBot(new RichsBotLogic(), "Rich");
			registerBot(new OlliesBotLogic(), "Ollie");
			registerBot(new cjbotlogic(), "CJ", 0x660099);
			registerBot(new StusBotLogic(), "Stu");*/
			registerBot(new DebugBot());
		}
		
		
		private function registerBot(botDef:BotDef):void
		{
			var botModel:BotModel;
			var body:b2Body;
			var radius:Number = 0.5;
			var density:Number = 13;
			var friction:Number = 1;
			var restitution:Number = 0.3;
			var view:Sprite;
			
			botModel = new BotModel(botDef.logic);
			botModel.name = botDef.name;
			botModel.colour = botDef.colour;
			botModel.radius = radius;
			
			view = new BotGraphic(botModel);
						
			body = createCircle(7, 6, radius, density, friction, restitution, botModel);			
			_model.addBot(body, view);
			
			if (botDef.name == "Debug") debugBot = body;
		}
		
		
		private function createBox(x:Number, y:Number, width:Number, height:Number, density:Number, friction:Number, restitution:Number, userData:*):b2Body
		{
			var body:b2Body;
			var bodyDef:b2BodyDef;
			var boxDef:b2PolygonDef;
			
			bodyDef = new b2BodyDef();
			bodyDef.position.x = x;
			bodyDef.position.y = y;
			
			//Top-down friction
			bodyDef.linearDamping = 1;
			bodyDef.angularDamping = 1;
			
			boxDef = new b2PolygonDef();
			boxDef.SetAsBox(width / 2, height / 2);
			boxDef.density = density;
			boxDef.friction = friction;
			boxDef.restitution = restitution;
			
			bodyDef.userData = userData;
			//bodyDef.userData.width = width; 
			//bodyDef.userData.height = height;
				 
			body = _model.world.CreateBody(bodyDef);
			body.CreateShape(boxDef);			
			body.SetMassFromShapes();
			
			return body;
		}
		
		
		private function createCircle(x:Number, y:Number, radius:Number, density:Number, friction:Number, restitution:Number, userData:*):b2Body
		{
			var body:b2Body;
			var bodyDef:b2BodyDef;
			var circleDef:b2CircleDef;
			
			bodyDef = new b2BodyDef();
			bodyDef.position.x = x;
			bodyDef.position.y = y;
			
			//Top-down friction
			bodyDef.linearDamping = 5;
			bodyDef.angularDamping = 5;
			
			circleDef = new b2CircleDef();
			circleDef.radius = radius;
			circleDef.density = density;
			circleDef.friction = friction;
			circleDef.restitution = restitution;
			
			bodyDef.userData = userData;
			//bodyDef.userData.width = radius * 2; 
			//bodyDef.userData.height = radius * 2; 
			
			body = _model.world.CreateBody(bodyDef);
			body.CreateShape(circleDef);					
			body.SetMassFromShapes();
			
			return body;	
		}
		
		
		private function moveBots():void
 		{
			var bot:b2Body;
			var angle:Number;
			var force:b2Vec2;
			var PI:Number = Math.PI;
			var twoPI:Number = PI * 2;
			
			for each (bot in _model.botList)
			{
				if (bot.m_userData.energy < 0) continue;
				
				bot.m_userData.logic.run();

				angle = bot.GetAngle();
				angle += PI;
				angle %= twoPI;
				if (angle < 0) angle += twoPI;
				angle -= PI;
				
				force = new b2Vec2();
				force.x = (bot.m_userData.enginePercent * MAX_SPEED) * Math.cos(angle);
				force.y = (bot.m_userData.enginePercent * MAX_SPEED) * Math.sin(angle);

				bot.ApplyForce(force, bot.GetWorldCenter());
				bot.ApplyTorque(bot.m_userData.rotatorPercent * MAX_TORQUE);
			}
 		}		
		
		
		private function checkScores():void
		{
			for each (var bot:b2Body in _model.botList)
			{
				if (bot.m_userData.energy < 0)
				{
					_model.removeBot(bot);
				}
				
				if (_model.botList.length == 1)
				{
					//_model.isPlaying = false;
					_model.updateWorld();
					//addChild(_trails);
					break;
				}
			}
		}
		
	}
}